home *** CD-ROM | disk | FTP | other *** search
/ PsL Monthly 1993 December / PSL Monthly Shareware CD-ROM (December 1993).iso / prgmming / dos / pascal / ovract.com / OVRACTM.PAS < prev    next >
Encoding:
Pascal/Delphi Source File  |  1989-11-22  |  18.8 KB  |  708 lines

  1. {
  2. OVRACTM is a variation on OVRACTR.  This program attempts to show, in a
  3. graphical sort of way, the relationships of the units in the overlay buffer.
  4. You will have to be the judge of just how useful the output of this program is
  5. for your particular application.
  6.  
  7. Each line of the output represents a change in the contents of the overlay
  8. buffer.  The left-most column shows elapsed program run time in minutes and
  9. seconds.  The rest of the line shows the units in the overlay buffer
  10. as much as possible to scale.  If the name of a unit does not fit
  11. within the space allotted, it is simply truncated.  This is the primary
  12. limitation of this program.  This can be overcome to some degree by specifying
  13. a large width parameter on the command line to increase the number of columns
  14. available.  Monitors that support more than 80 columns can be used to good
  15. advantage here.  Alternatively, the output file can then be viewed using an
  16. editor or file viewer that allows horizontal scrolling.
  17.  
  18. Except for the addition of the width parameter, OVRACTM is used the same way
  19. as OVRACTR.
  20.  
  21. Call OVRACTM as follows:
  22.  
  23.   OVRACTM [Options] [OutputWidth] ProgName [>Output]
  24.  
  25. OVRACTM forces the extension 'MAP' onto ProgName to find the MAP file, and
  26. the extension 'OVD' onto ProgName to find the overlay data file. The overlay
  27. report is written to the standard output and may be redirected to a file or
  28. to the printer.  The optional width parameter is an integer specifying the
  29. number of columns to be used for the output.  The options are
  30.  
  31.    /Q   stops OVRACTM from writing status messages while it works.
  32.    /D   produces the detailed report of all overlay activity.
  33.    /S   produces the summary report showing statistics for each unit.
  34.    /O   produces the summary report showing statistics only for overlaid units
  35.  
  36. Written by Ron Schuster (CIS 76666,2322).  Copyright (c) 1989.
  37. All rights reserved.  May be distributed freely, but not for a profit.
  38.  
  39. This program was originally based on the overlay profiler OVRPROF
  40. written by Richard Casey (CIS 72247,151).
  41.  
  42. Portions of this program originally appeared in OVRSIZ by Kim Kokkonen,
  43. TurboPower Software (CIS 76004,2611), and were used with the permission of
  44. the author.  Copyright (c) 1989, TurboPower Software. All rights reserved.
  45. May be distributed freely, but not for a profit.
  46.  
  47.  
  48. Version 1.0, 11/21/89
  49. --------------------
  50.   Initial release.
  51. }
  52.  
  53. {$R-,S-,I-,V-,F-,B-}
  54.  
  55. program OVRACTM;
  56.   {-Generate reports of overlay activity from data produced by OVRACT}
  57. uses Dos;
  58. const
  59.   Version = '1.0';                {Version number}
  60.   MaxUnits = 255;                 {Maximum number of units to report}
  61.   NameSize = 15;                  {Maximum reported segment name length}
  62.   BufSize = 1024;                 {Size of text I/O buffer}
  63.   ShowStatus : Boolean = True;    {True to keep status running during operation}
  64.   ShowDetail : Boolean = False;   {True to write detailed dump of overlay events}
  65.   ShowSummary : Boolean = False;  {True to write summary report}
  66.   OverlaysOnly : Boolean = False; {True to write summary report with overlaid
  67.                                     units only}
  68.   Digits : array[0..$F] of Char = '0123456789ABCDEF';
  69.   DosDelimSet : set of Char = ['\', ':', #0];
  70.   LeftMargin = 9;                 {Columns used by time in left margin}
  71.  
  72. type
  73.   UnitNameStr = string[NameSize];
  74.   UnitRecord = record
  75.     Name : UnitNameStr;
  76.     SegClass : Word;
  77.     StatSeg : Word;
  78.     StatLen : LongInt;
  79.     FileOfs : LongInt;
  80.     CodeSize : Word;
  81.     FixupSize : Word;
  82.     EntryPts : Word;
  83.     LoadCount : Word;
  84.     ReprieveCount : Word;
  85.   end;
  86.  
  87.   Long =
  88.     record
  89.       LowWord, HighWord : Word;
  90.     end;
  91.  
  92. var
  93.   UnitCount    : Word;
  94.   Units        : array[0..MaxUnits] of UnitRecord;
  95.   StdErr       : text;
  96.   OvrDataFile  : file of Word;
  97.   PSP          : Word;
  98.   Ovr_Heap_Org : Word;
  99.   Ovr_Heap_End : Word;
  100.   Buffer       : array[1..BufSize] of Char; {Text buffer for map file}
  101.   Dname        : PathStr;               {Input OVD file name}
  102.   Mname        : PathStr;               {Input MAP file name}
  103.   Time         : LongInt;
  104.   Width        : Integer;
  105.   OutputLine   : string;
  106.   ParaPerCol   : Real;
  107.  
  108. procedure WriteCopyRight;
  109.   {-Copyright notice in object code and on screen}
  110. begin
  111.   WriteLn(StdErr, 'TP5.x Overlay Activity Mapper, by Ron Schuster. Version ', Version);
  112. end;
  113.  
  114. procedure OpenStdErr;
  115.   {-Open standard error device}
  116. begin
  117.   Assign(StdErr, '');
  118.   Rewrite(StdErr);
  119.   with TextRec(StdErr) do begin
  120.     Handle := 2;
  121.     BufSize := 1;
  122.   end;
  123. end;
  124.  
  125. procedure Error(Msg : String);
  126.   {-Report error and halt}
  127. begin
  128.   if Msg <> '' then
  129.     WriteLn(StdErr, Msg);
  130.   Halt(1);
  131. end;
  132.  
  133. procedure InvalidMapError;
  134.   {-Common error}
  135. begin
  136.   Error('Invalid MAP file format');
  137. end;
  138.  
  139. function HexW(W : Word) : String;
  140.   {-Return hex string for word}
  141. begin
  142.   HexW[0] := #4;
  143.   HexW[1] := Digits[Hi(W) shr 4];
  144.   HexW[2] := Digits[Hi(W) and $F];
  145.   HexW[3] := Digits[Lo(W) shr 4];
  146.   HexW[4] := Digits[Lo(W) and $F];
  147. end;
  148.  
  149. function HexL(L : LongInt) : String;
  150.   {-Return hex string for LongInt}
  151. begin
  152.   with Long(L) do
  153.     HexL := HexW(HighWord)+HexW(LowWord);
  154. end;
  155.  
  156. function Long2Str(L : LongInt) : String;
  157.   {-Convert a long/word/integer/byte/shortint to a string}
  158. var
  159.   S : String;
  160. begin
  161.   Str(L, S);
  162.   Long2Str := S;
  163. end;
  164.  
  165. function StUpcase(S : String) : String;
  166.   {-Return the uppercase of a string}
  167. var
  168.   I : Integer;
  169. begin
  170.   for I := 1 to Length(S) do
  171.     S[I] := Upcase(S[I]);
  172.   StUpcase := S;
  173. end;
  174.  
  175. function StLocase(S : String) : String;
  176.   {-Return the lowercase of a string}
  177. var
  178.   I : Integer;
  179. begin
  180.   for I := 1 to Length(S) do
  181.     if (S[I] >= 'A') and (S[I] <= 'Z') then
  182.       S[I] := chr(ord(S[I])+32);
  183.   StLocase := S;
  184. end;
  185.  
  186. function PadCh(S : String; Ch : Char; Len : Byte) : String;
  187.   {-Return a string right-padded to length len with ch}
  188. var
  189.   O : String;
  190. begin
  191.   if Length(S) >= Len then
  192.     PadCh := S
  193.   else begin
  194.     O[0] := Chr(Len);
  195.     Move(S[1], O[1], Length(S));
  196.     FillChar(O[Succ(Length(S))], Len-Length(S), Ch);
  197.     PadCh := O;
  198.   end;
  199. end;
  200.  
  201. function Pad(S : string; Len : Byte) : string;
  202.   {-Return a string right-padded to length len with blanks}
  203. begin
  204.   Pad := PadCh(S, ' ', Len);
  205. end;
  206.  
  207. function TrimLead(S : String) : String;
  208.   {-Return a string with leading white space removed}
  209. begin
  210.   while (Length(S) > 0) and (S[1] <= ' ') do
  211.     Delete(S, 1, 1);
  212.   TrimLead := S;
  213. end;
  214.  
  215. function TrimTrail(S : string) : string;
  216.   {-Return a string with trailing white space removed}
  217. var
  218.   SLen : Byte absolute S;
  219. begin
  220.   while (SLen > 0) and (S[SLen] <= ' ') do
  221.     Dec(SLen);
  222.   TrimTrail := S;
  223. end;
  224.  
  225. function Trim(S : String) : String;
  226.   {-Return a string with leading and trailing white space removed}
  227. begin
  228.   while (Length(S) > 0) and (S[Length(S)] <= ' ') do
  229.     Dec(S[0]);
  230.   while (Length(S) > 0) and (S[1] <= ' ') do
  231.     Delete(S, 1, 1);
  232.   Trim := S;
  233. end;
  234.  
  235. function GetLong(var S : String; var L : LongInt) : Boolean;
  236.   {-Parse next longint out of line S}
  237. var
  238.   Num : String[8];
  239.   Code : Word;
  240. begin
  241.   S := TrimLead(S);
  242.   Num := '';
  243.   while (Length(S) > 0) and (Pos(S[1], Digits) <> 0) do begin
  244.     Num := Num+S[1];
  245.     Delete(S, 1, 1);
  246.   end;
  247.   if Length(Num) = 0 then begin
  248.     GetLong := False;
  249.     Exit;
  250.   end;
  251.   if (Length(S) > 0) and (Upcase(S[1]) = 'H') then begin
  252.     Num := '$'+Num;
  253.     Delete(S, 1, 1);
  254.   end;
  255.   Val(Num, L, Code);
  256.   GetLong := (Code = 0);
  257. end;
  258.  
  259. function GetInt(var S : String; var I : Integer) : Boolean;
  260.   {-Parse next integer out of line S}
  261. var
  262.   L : LongInt;
  263. begin
  264.   GetInt := False;
  265.   if not GetLong (S, L) then
  266.     Exit;
  267.   if (L < -MaxInt) or (L > MaxInt) then
  268.     Exit;
  269.   I := L;
  270.   GetInt := True;
  271. end;
  272.  
  273. function GetName(var S, Name : String) : Boolean;
  274.   {-Parse next alphanumeric name from string s}
  275. begin
  276.   S := TrimLead(S);
  277.   Name := '';
  278.   while (Length(S) > 0) and (S[1] > ' ') do begin
  279.     if Length(Name) < NameSize then
  280.       Name := Name+S[1];
  281.     Delete(S, 1, 1);
  282.   end;
  283.   GetName := (Name <> '');
  284. end;
  285.  
  286. function HasExtension(Name : String; var DotPos : Word) : Boolean;
  287.   {-Return whether and position of extension separator dot in a pathname}
  288. var
  289.   I : Word;
  290. begin
  291.   DotPos := 0;
  292.   for I := Length(Name) downto 1 do
  293.     if (Name[I] = '.') and (DotPos = 0) then
  294.       DotPos := I;
  295.   HasExtension := (DotPos > 0) and (Pos('\', Copy(Name, Succ(DotPos), 64)) = 0);
  296. end;
  297.  
  298. function ForceExtension(Name, Ext : String) : String;
  299.   {-Return a pathname with the specified extension attached}
  300. var
  301.   DotPos : Word;
  302. begin
  303.   if HasExtension(Name, DotPos) then
  304.     ForceExtension := Copy(Name, 1, DotPos)+Ext
  305.   else
  306.     ForceExtension := Name+'.'+Ext;
  307. end;
  308.  
  309. procedure WriteHelp;
  310.   {-Display help information and halt}
  311. begin
  312.   WriteLn;
  313.   WriteLn('Usage: OVRACTM [Options] [OutputWidth] InputName [>OutputFile]');
  314.   WriteLn;
  315.   WriteLn('  OVRACTM must read:');
  316.   WriteLn('    InputName.MAP - symbol file for segment information.');
  317.   WriteLn('    InputName.OVD - overlay data file produced by OVRACT.');
  318.   WriteLn;
  319.   WriteLn('Options:');
  320.   WriteLn('  /Q    Quiet mode. No status output while processing.');
  321.   WriteLn('  /D    Detail report showing all overlay activity.');
  322.   WriteLn('  /S    Summary report showing statistics for each unit.');
  323.   WriteLn('  /O    Summary report showing statistics only for overlaid units.');
  324.   WriteLn;
  325.   WriteLn('  At least one report option must be specified');
  326.   Halt(1);
  327. end;
  328.  
  329. function ExistFile(FName : String) : Boolean;
  330.   {-Return true if file exists}
  331. var
  332.   F : file;
  333. begin
  334.   Assign(F, FName);
  335.   Reset(F);
  336.   if IoResult = 0 then begin
  337.     ExistFile := True;
  338.     Close(F);
  339.   end else
  340.     ExistFile := False;
  341. end;
  342.  
  343. procedure ValidateInput;
  344.   {-Get working filenames and assure files exist}
  345. var
  346.   Iroot : PathStr;
  347.   Arg : String;
  348.   I : Integer;
  349. begin
  350.   {Get parameters}
  351.   Width := 79;
  352.   Iroot := '';
  353.   I := 1;
  354.   while I <= ParamCount do begin
  355.     Arg := StUpcase(ParamStr(I));
  356.     if (Arg = '/Q') or (Arg = '-Q') then
  357.       ShowStatus := False
  358.     else if (Arg = '/D') or (Arg = '-D') then
  359.       ShowDetail := True
  360.     else if (Arg = '/S') or (Arg = '-S') then
  361.       ShowSummary := True
  362.     else if (Arg = '/O') or (Arg = '-O') then begin
  363.       ShowSummary := True;
  364.       OverlaysOnly := True;
  365.     end
  366.     else if (Arg[1] in ['0'..'9']) then begin
  367.       if not GetInt (Arg, Width) then
  368.         Error('Invalid numeric argument');
  369.     end
  370.     else if Iroot = '' then
  371.       Iroot := Arg
  372.     else
  373.       Error('Too many filenames on command line');
  374.     Inc(I);
  375.   end;
  376.   if (Iroot = '') or not (ShowDetail or ShowSummary) then
  377.     WriteHelp;
  378.  
  379.   {Build working filenames}
  380.   Dname := ForceExtension(Iroot, 'OVD');
  381.   Mname := ForceExtension(Iroot, 'MAP');
  382.  
  383.   {Make sure files are OK}
  384.   if not ExistFile(Dname) then
  385.     Error('OVD file '+Dname+' not found');
  386.   if not ExistFile(Mname) then
  387.     Error('MAP file '+Mname+' not found');
  388.  
  389.   Width := Width - LeftMargin;
  390.   if (Width < 1) or (Width > 255) then
  391.     Error('Width paramenter out of range');
  392. end;
  393.  
  394. procedure ParseMapFile(FName : String);
  395.   {-Read and parse the MAP file, guaranteed to exist}
  396. var
  397.   F : Text;
  398.   S : String;
  399.   StatStart : LongInt;
  400.   SegType : String;
  401.   Tlong : LongInt;
  402.   ParseState : (Unknown, Segments, Done);
  403. begin
  404.  
  405.   {Open up the MAP file for reading}
  406.   Assign(F, FName);
  407.   SetTextBuf(F, Buffer, BufSize);
  408.   Reset(F);
  409.   if IoResult <> 0 then
  410.     Error('Error opening '+FName);
  411.  
  412.   if ShowStatus then
  413.     WriteLn(StdErr, 'Parsing MAP file');
  414.  
  415.   {Parse the segment description section only}
  416.   UnitCount := 0;
  417.   ParseState := Unknown;
  418.   repeat
  419.     ReadLn(F, S);
  420.     if IoResult <> 0 then
  421.       Error('Error reading '+FName);
  422.     S := StUpcase(Trim(S));
  423.     if S <> '' then
  424.       if Pos('START', S) = 1 then
  425.         ParseState := Segments
  426.       else if Pos('ADDRESS', S) = 1 then
  427.         ParseState := Done
  428.       else if ParseState = Segments then begin
  429.         {Parse the line to get the unit description}
  430.         Inc(UnitCount);
  431.         if UnitCount > MaxUnits then
  432.           Error('Cannot exceed '+Long2Str(MaxUnits)+' segments');
  433.         FillChar(Units[UnitCount], SizeOf(UnitRecord), 0);
  434.  
  435.         with Units[UnitCount] do begin
  436.  
  437.           {Get the position and size of the unit in the EXE image}
  438.           if not GetLong(S, StatStart) then
  439.             InvalidMapError;
  440.           StatSeg := StatStart shr 4;
  441.           {Ignore the end of the segment}
  442.           if not GetLong(S, Tlong) then
  443.             InvalidMapError;
  444.           {Get the length of the segment}
  445.           if not GetLong(S, StatLen) then
  446.             InvalidMapError;
  447.  
  448.           {Get the name of the segment}
  449.           if not GetName(S, Name) then
  450.             InvalidMapError;
  451.           Name := StLoCase(Name);
  452.           Name[1] := Upcase(Name[1]);
  453.  
  454.           {Some segments are not really in the EXE file}
  455.           if not GetName(S, SegType) then
  456.             InvalidMapError;
  457.           if SegType = 'CODE' then
  458.             SegClass := 0
  459.           else if SegType = 'DATA' then
  460.             SegClass := 1
  461.           else if SegType = 'STACK' then
  462.             SegClass := 2
  463.           else if SegType = 'HEAP' then
  464.             SegClass := 3
  465.           else
  466.             SegClass := 4;
  467.         end;
  468.       end;
  469.   until (ParseState = Done) or EoF(F);
  470.   Close(F);
  471. end;
  472.  
  473. function ReadWord : Word;
  474. var
  475.   W : Word;
  476. begin
  477.   if EOF (OvrDataFile) then
  478.     Error ('Unexpected EOF on ' + Dname);
  479.   Read (OvrDataFile, W);
  480.   ReadWord := W;
  481. end;
  482.  
  483. function LookupSeg (S : Word) : Integer;
  484. var
  485.   I : Integer;
  486. begin
  487.   for I := 1 to UnitCount do
  488.     if (S = Units[I].StatSeg) and (Units[I].StatLen > 0) then begin
  489.       LookUpSeg := I;
  490.       exit;
  491.     end;
  492.   LookUpSeg := 0;
  493. end;
  494.  
  495. function NextPara(Bytes : LongInt) : LongInt;
  496.   {-Round up to next paragraph}
  497. begin
  498.   NextPara := (Bytes+15) and $FFFFFFF0;
  499. end;
  500.  
  501. procedure ProcessCodeList;
  502. var
  503.   StaticSeg : Word;
  504. begin
  505.   StaticSeg := ReadWord;
  506.   while StaticSeg <> 0 do begin
  507.     with Units[LookupSeg(StaticSeg)] do begin
  508.       with Long(FileOfs) do begin
  509.         LowWord := ReadWord;
  510.         HighWord := ReadWord;
  511.       end;
  512.       CodeSize := NextPara(ReadWord);
  513.       FixupSize := NextPara(ReadWord);
  514.       EntryPts := ReadWord;
  515.     end;
  516.     StaticSeg := ReadWord;
  517.   end;
  518. end;
  519.  
  520. function FormatTime (T : LongInt) : string;
  521. const
  522.   Divisor = 119318.0/65536;
  523. var
  524.   Tenths : LongInt;
  525.   Minutes, I : Integer;
  526.   S : string[8];
  527.   Secs : string[3];
  528. begin
  529.   Tenths := Round (T / Divisor);
  530.   Minutes := Tenths div 600;
  531.   Tenths := Tenths mod 600;
  532.   Str (Minutes:3, S);
  533.   Str (Tenths:3, Secs);
  534.   S := S + ':' + Secs;
  535.   for I := 1 to 6 do
  536.     if S[I] = ' ' then
  537.       S[I] := '0';
  538.   Insert ('.',S,7);
  539.   FormatTime := S;
  540. end;
  541.  
  542. procedure ChangeOverlayBuffer;
  543. begin
  544.   Ovr_Heap_Org := ReadWord;
  545.   Ovr_Heap_End := ReadWord;
  546.   if ShowDetail then begin
  547.     Writeln ('Overlay buffer set to ',HexW(Ovr_Heap_Org),'-',HexW(Ovr_Heap_End));
  548.     ParaPerCol := (Ovr_Heap_End - Ovr_Heap_Org) / Width;
  549.   end;
  550. end;
  551.  
  552. function SegToCol (S : Word) : Integer;
  553. begin
  554.   SegToCol := Trunc((S - Ovr_Heap_Org)/ParaPerCol);
  555. end;
  556.  
  557. procedure FormatUnit (StaticSeg,LoadSeg:Word);
  558. var
  559.   I : Integer;
  560.   StartCol : Integer;
  561.   Len : Integer;
  562.   Name : String;
  563. begin
  564.   I := LookupSeg (StaticSeg-PSP-$10);
  565.   StartCol := SegToCol (LoadSeg);
  566.   if I = 0 then begin
  567.     Len := 4;
  568.     Name := HexW (StaticSeg);
  569.   end
  570.   else begin
  571.     Len := SegToCol (LoadSeg + NextPara (Units[I].CodeSize) shr 4) - StartCol;
  572.     if Len < 1 then
  573.       Len := 1;
  574.     Name := PadCh (Units[I].Name,'-',Len);
  575.   end;
  576.   Move (Name[1], OutputLine[StartCol + 1], Len);
  577. end;
  578.  
  579. procedure PrintLoadList (W : Word);
  580. var
  581.   I : Integer;
  582.   StaticSeg,
  583.   LoadSeg : Word;
  584.   OvrSeg : Word;  { Static segment of unit just loaded }
  585.   OvrOfs : Word;  { Offset of procedure within the unit's static segment }
  586. begin
  587.   OvrSeg := W;
  588.   OvrOfs := ReadWord;
  589.   StaticSeg := ReadWord;
  590.   I := LookupSeg (OvrSeg - PSP - $10);
  591.   with Units[I] do begin
  592.     if ShowDetail then
  593.       OutputLine := Pad ('', Width);
  594.     if StaticSeg = 0 then begin
  595.       { there is no load list, must be a reprieve }
  596.       Inc(ReprieveCount);
  597.       Exit;  { Don't print reprieve events on map }
  598.     end
  599.     else begin
  600.       { load list follows }
  601.       Inc(LoadCount);
  602.       while StaticSeg <> 0 do begin
  603.         LoadSeg := ReadWord;
  604.         if ShowDetail then
  605.           FormatUnit (StaticSeg, LoadSeg);
  606.         StaticSeg := ReadWord;
  607.       end;
  608.     end;
  609.   end;
  610.   if ShowDetail then
  611.     Writeln (FormatTime (Time), ' ', TrimTrail(OutputLine));
  612. end;
  613.  
  614. procedure ProcessOverlayData (Name : PathStr);
  615. const
  616.   EndListMark : Word = 0;
  617.   OvrHeapMark : Word = $FFFF;
  618. var
  619.   W : Word;
  620. begin
  621.   assign(OvrDataFile,Name);
  622.   {$I-}
  623.   reset(OvrDataFile);
  624.   {$I+}
  625.   if IOResult <> 0 then
  626.     Error ('Could not open '+Name+' for input')
  627.   else begin
  628.     if ShowStatus then
  629.       WriteLn(StdErr, 'Reading OVD file');
  630.     if ReadWord <> 1 then
  631.       Error ('OVD file version mismatch');
  632.     PSP := ReadWord;
  633.     ProcessCodeList;
  634.     while not EOF (OvrDataFile) do begin
  635.       with Long(Time) do begin
  636.         LowWord := ReadWord;
  637.         HighWord := ReadWord;
  638.       end;
  639.       W := ReadWord;
  640.       if W = OvrHeapMark then begin
  641.         ChangeOverlayBuffer;
  642.         W := ReadWord;
  643.       end;
  644.       PrintLoadList (W);
  645.     end;
  646.   end;
  647. end;
  648.  
  649. procedure WriteUnitInfo;
  650. var
  651.   V : Word;
  652. begin
  653.   WriteLn;
  654.   WriteLn(
  655. 'UNIT STATISTICS');
  656.   WriteLn(
  657. '                Static  Static  Overlay  Fixup   Entry  Overlay  Load  Reprieve');
  658.   WriteLn(
  659. 'Segment name   Segment    Size    Size    Size  Points  FilePos  Count  Count');
  660.   WriteLn(
  661. '==============  ======   =====   =====   =====   =====  =======  =====  =====');
  662. {xxxxxxxxxxxxxxx 0FFFFh   ddddd   ddddd   ddddd   ddddd  0FFFFFh  ddddd  ddddd}
  663.  
  664.   for V := 1 to UnitCount do
  665.     with Units[V] do
  666.       if (StatLen > 0) and (not OverlaysOnly or (CodeSize > 0)) then begin
  667.         Write(Pad(Name, NameSize+1),
  668.               '0', HexW(StatSeg), 'h   ',
  669.               StatLen:5, '   ');
  670.  
  671.         if CodeSize > 0 then begin
  672.           { Overlaid Unit }
  673.           Write(CodeSize:5, '   ',
  674.                 FixupSize:5, '   ',
  675.                 EntryPts:5, '  ',
  676.                 Copy(HexL(FileOfs), 3, 6), 'h  ',
  677.                 LoadCount:5, '  ',
  678.                 ReprieveCount:5);
  679.  
  680.         end else begin
  681.           {Non-overlaid unit or other segment}
  682.           Write('    -       -       -        -      -      -');
  683.         end;
  684.         WriteLn;
  685.       end;
  686. end;
  687.  
  688. begin
  689.   {Open standard error device}
  690.   OpenStdErr;
  691.  
  692.   {Display copyright}
  693.   WriteCopyRight;
  694.  
  695.   {Get filenames and assure they exist}
  696.   ValidateInput;
  697.  
  698.   {Parse MAP file to get segment names and locations}
  699.   ParseMapFile(Mname);
  700.  
  701.   {Read overlay data file}
  702.   ProcessOverlayData(Dname);
  703.  
  704.   {Write information}
  705.   if ShowSummary then
  706.     WriteUnitInfo;
  707. end.
  708.